package pso.base;


import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import problema.Problema;
import pso.operador.equacoes.EquacaoComC1C2Diferentes;
import pso.operador.equacoes.EquacaoComC1C2Iguais;

public class AlgoritmoPSO {
	private List<Particula> enxame;
	private int qtdParticulas;

	private Inicializacao inicializacao;

	private static int MAXIMA_FIT_EVAL = 5000;

	private Problema problema;
	private EquacaoVelocidade equacao;
	private Topologia topologia;
	
	private double[] valoresPorInteracao;

	private int pontos;
	private static int QUANTIDADE_DADOS = 50;

	public AlgoritmoPSO(int qtdParticulas, Inicializacao inicializacao, Problema problema, 
			Topologia topologia, EquacaoVelocidade equacao){
		this.qtdParticulas = qtdParticulas;
		this.inicializacao = inicializacao;
		this.enxame = new ArrayList<Particula>();
		this.problema = problema;
		this.topologia = topologia;
		this.equacao = equacao;
		
		valoresPorInteracao = new double[QUANTIDADE_DADOS];
	}

	public void execute(){
		inicializacao.executar(qtdParticulas, enxame, problema);
			
		int currentFitAval = 0;
		int currentInteracao = 1;
		
		int maximaIteracao = MAXIMA_FIT_EVAL/qtdParticulas;

		this.pontos = maximaIteracao/QUANTIDADE_DADOS;
		valoresPorInteracao = new double[QUANTIDADE_DADOS];
		
		if(equacao instanceof EquacaoComC1C2Iguais){
			((EquacaoComC1C2Iguais)equacao).setTotalInteracoes(maximaIteracao);
		}
		else if(equacao instanceof EquacaoComC1C2Diferentes){
			((EquacaoComC1C2Diferentes)equacao).setTotalInteracoes(maximaIteracao);
		}
		
		while(currentFitAval < MAXIMA_FIT_EVAL){

			for(int i=0; i<enxame.size(); i++){
				Particula particula = enxame.get(i);
								
				//avalia o fitness e atribui o valor a particula
				problema.avaliacaoFitness(particula);
				currentFitAval++;
				if(currentFitAval == MAXIMA_FIT_EVAL){
					break;
				}
				
				//Define Pbest
				//Caso o fitness seja melhor do que o ja existente, atualiza-o assim como a posicao
				if(particula.getFitness() < particula.getBestFitness()){
					particula.setBestFitness(particula.getFitness());
					particula.setMelhorPosicaoParticula(particula.getPosicao());
				}

				//Define Gbest 
				//Melhor particula existente (das permitidas que se possa ver)
				Particula gBest = topologia.getMelhor(enxame, i);
				particula.setMelhorPosicaoGlobalVizualizada(gBest.getPosicao());			
			}
			
			for(int i=0; i<enxame.size(); i++){
				Particula particula = enxame.get(i);
				
				equacao.calcularNovaPosicao(particula, currentInteracao);
			}
	
			imprimirFitness(currentInteracao++);
		}
	}
	
	/**
	 * @return the valoresPorInteracao
	 */
	public double[] getValoresPorInteracao() {
		return valoresPorInteracao;
	}

	/**
	 * @param valoresPorInteracao the valoresPorInteracao to set
	 */
	public void setValoresPorInteracao(double[] valoresPorInteracao) {
		this.valoresPorInteracao = valoresPorInteracao;
	}
	
	private double melhorFitness(){
		return melhorIndividuo().getFitness();
	}

	private Particula melhorIndividuo(){
		List<Particula> copia = new ArrayList<Particula>();
		
		for(Particula p : enxame){
			copia.add(p);
		}
		
		Collections.sort(copia);

		return copia.get(0);
	}

	private void imprimirFitness(int iteracaoAtual){
		if(iteracaoAtual % pontos == 0){
			//System.out.println(melhorFitness());
			if(iteracaoAtual > 0){			
				valoresPorInteracao[(iteracaoAtual/pontos)-1] = melhorFitness();
			}
		}
	}
}
